<!--
> Muaz Khan     - www.MuazKhan.com
> MIT License   - www.WebRTC-Experiment.com/licence
> Documentation - github.com/muaz-khan/DetectRTC
> npm install detectrtc
> bower install detectrtc
-->
<!DOCTYPE html>
<html lang="en">
    <head>
        <title>DetectRTC.js / Detect WebRTC features! ® Muaz Khan</title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
        <link rel="author" type="text/html" href="https://plus.google.com/+MuazKhan">
        <meta name="author" content="Muaz Khan">
        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
        
        <link rel="stylesheet" href="https://cdn.webrtc-experiment.com/style.css">
        
        <style>
            audio {
                vertical-align: bottom;
                width: 10em;
            }

            video {
                max-width: 100%;
                vertical-align: top;
            }

            input {
                border: 1px solid #d9d9d9;
                border-radius: 1px;
                font-size: 2em;
                margin: .2em;
                width: 30%;
            }

            p, .inner { padding: 1em; }

            li {
                border-bottom: 1px solid rgb(189, 189, 189);
                border-left: 1px solid rgb(189, 189, 189);
                padding: .5em;
            }

            label {
                display: inline-block;
                width: 8em;
            }

            .info-div {
                display: inline-block;
                vertical-align: middle;
                background-repeat: no-repeat;
                background-image: url(https://i.imgur.com/tnMN9tG.png?1);
                background-position: center center;
                width: 32px;
                height: 32px;
            }

            .inline-pre {
                margin: 0;
                padding: 0;
                border: 0;
            }
        </style>
        <script>
            document.createElement('article');
            document.createElement('footer');
        </script>
        
        <!-- 
            Script used to detect WebRTC features!
            https://github.com/muaz-khan/DetectRTC#how-to-link
        -->
        <script src="https://cdn.webrtc-experiment.com/DetectRTC.js"> </script>
        <script src="https://cdn.webrtc-experiment.com/screenshot.js"></script>
        <script src="https://cdn.webrtc-experiment.com/gumadapter.js"></script>

        <!-- scripts to highlight syntax -->
        <script src="https://cdn.webrtc-experiment.com/syntax/sh_main.min.js" type="text/javascript"> </script>
        <script src="https://cdn.webrtc-experiment.com/syntax/sh_javascript.min.js" type="text/javascript"> </script>
        <script src="https://cdn.webrtc-experiment.com/syntax/sh_html.min.js" type="text/javascript"> </script>
        <link href="https://cdn.webrtc-experiment.com/syntax/sh_style.css" type="text/css" rel="stylesheet">
    </head>

    <body onload="sh_highlightDocument();">
        <article>
            <header style="text-align: center;">
                <h1>
                    <a href="https://github.com/muaz-khan/DetectRTC">DetectRTC.js</a>: Detect <a href="https://www.webrtc-experiment.com/" target="_blank">WebRTC</a> features! ® 
                    <a href="https://github.com/muaz-khan" target="_blank">Muaz Khan</a>
                </h1>            
                <p>
                    <a href="https://www.webrtc-experiment.com/">HOME</a>
                    <span> &copy; </span>
                    <a href="http://www.MuazKhan.com/" target="_blank">Muaz Khan</a>
                    
                    .
                    <a href="http://twitter.com/WebRTCWeb" target="_blank" title="Twitter profile for WebRTC Experiments">@WebRTCWeb</a>
                    
                    .
                    <a href="https://github.com/muaz-khan?tab=repositories" target="_blank" title="Github Profile">Github</a>
                    
                    .
                    <a href="https://github.com/muaz-khan/DetectRTC/issues?state=open" target="_blank">Latest issues</a>
                    
                    .
                    <a href="https://github.com/muaz-khan/DetectRTC/commits/master" target="_blank">What's New?</a>
                </p>
            </header>

            <div class="github-stargazers"></div>

            <div style="text-align:center;">
                <a href="https://www.npmjs.com/package/detectrtc" target="_blank">
                    <img src="https://img.shields.io/npm/v/detectrtc.svg">
                </a>

                <a href="https://www.npmjs.com/package/detectrtc" target="_blank">
                    <img src="https://img.shields.io/npm/dm/detectrtc.svg">
                </a>

                <a href="https://travis-ci.org/muaz-khan/DetectRTC">
                    <img src="https://travis-ci.org/muaz-khan/DetectRTC.png?branch=master">
                </a>
            </div>
            
            <section class="experiment" id="detectrtc-output">
                <button id="save" style="float:right;">Print & Save As Image</button>
                <h2 id="welcome">DetectRTC!</h2>
                <table id="browser-features"></table>
            </section>
            
            <!-- dirty trick to bypass webrtc blockers -->
            <iframe id="iframe" sandbox="allow-same-origin" style="display: none"></iframe>
            
            <script>
                var browserFeaturesTable = document.querySelector('#browser-features');

                function appendTR(firstValue, secondValue) {
                    var tr = document.createElement('tr');
                    tr.innerHTML = '<td style="padding:5px;max-width:' + (parseInt(innerWidth / 2) -  180) + 'px; overflow:hidden;">' + firstValue + '</td><td style="padding:5px;">' + secondValue + '</td>';
                    browserFeaturesTable.appendChild(tr);
                    return tr;
                }

                function printVal(value) {
                    return value == true ? 'Yep' : value == false ? 'Nope' : value;
                }

                function getInfoDiv(id) {
                    return '<div class="info-div" id="' + id + '"></div>';
                }

                var output = '';

                function onDetectRTCLoaded() {
                    browserFeaturesTable.innerHTML = '';

                    appendTR('Operating System', printVal(DetectRTC.osName) + ' version: ' + printVal(DetectRTC.osVersion));
                    appendTR('Browser', printVal(DetectRTC.browser.name) + ' version: ' + printVal(DetectRTC.browser.fullVersion) + '<br>Private browsing? ' + printVal(DetectRTC.browser.isPrivateBrowsing));

                    appendTR('Display resolutions', printVal(DetectRTC.displayResolution));

                    output = printVal(DetectRTC.hasSpeakers);
                    if(DetectRTC.audioOutputDevices.length) {
                        output += '<br>Found speaker devices: ' + DetectRTC.audioOutputDevices.length;

                        var labels = [];
                        DetectRTC.audioOutputDevices.forEach(function(device) {
                           labels.push(device.label);
                        });

                        output += '<br><div style="margin-left:15px;">' + labels.join('<br>') + '</div>';
                    }
                    appendTR('System has Speakers?', output);

                    output = printVal(DetectRTC.hasMicrophone);
                    if(DetectRTC.audioInputDevices.length) {
                        output += '<br>Found microphone devices: ' + DetectRTC.audioInputDevices.length;

                        var labels = [];
                        DetectRTC.audioInputDevices.forEach(function(device) {
                           labels.push(device.label);
                        });

                        output += '<br><div style="margin-left:15px;">' + labels.join('<br>') + '</div>';
                    }
                    appendTR('System has Microphone?', output);
                    
                    output = printVal(DetectRTC.hasWebcam);
                    if(DetectRTC.videoInputDevices.length) {
                        output += '<br>Found webcam devices: ' + DetectRTC.videoInputDevices.length;

                        var labels = [];
                        DetectRTC.videoInputDevices.forEach(function(device) {
                           labels.push(device.label);
                        });

                        output += '<br><div style="margin-left:15px;">' + labels.join('<br>') + '</div>';
                    }
                    appendTR('System has Webcam?', output);

                    appendTR('Website has webcam permissions?', printVal(DetectRTC.isWebsiteHasWebcamPermissions));
                    appendTR('Website has mcirophone permissions?', printVal(DetectRTC.isWebsiteHasMicrophonePermissions));
                    
                    appendTR('Browser allows getUserMedia on this page?', printVal(DetectRTC.isGetUserMediaSupported));

                    appendTR('Can you change output audio devices?' + getInfoDiv('infoIcon-set-sink-id'), printVal(DetectRTC.isSetSinkIdSupported));

                    appendTR('Can you change camera resolutions without making new getUserMedia request?' + getInfoDiv('infoIcon-apply-constraints'), printVal(DetectRTC.isApplyConstraintsSupported));

                    appendTR('Browser Supports WebRTC (Either 1.0 or 1.1)?', printVal(DetectRTC.isWebRTCSupported));
                    appendTR('Browser Supports ORTC (WebRTC 1.1)?', printVal(DetectRTC.isORTCSupported));

                    appendTR('Can you replace tracks without renegotiating peers?' + getInfoDiv('infoIcon-replace-tracks'), printVal(DetectRTC.isRTPSenderReplaceTracksSupported));

                    appendTR('Can your browser record remote audio or process remote audio stream in WebAudio API?', printVal(DetectRTC.isRemoteStreamProcessingSupported));

                    appendTR('Browser Supports WebSockets API?', printVal(DetectRTC.isWebSocketsSupported));
                    
                    var tr = appendTR('Your system blocked WebSockets protocol or WebSockets server is not accessible?', printVal(DetectRTC.isWebSocketsBlocked));
                    DetectRTC.checkWebSocketsSupport(function() {
                        tr.querySelectorAll('td')[1].innerHTML = printVal(DetectRTC.isWebSocketsBlocked);
                    });

                    appendTR('Browser Supports WebAudio API?', printVal(DetectRTC.isAudioContextSupported));
                    appendTR('Browser Supports SCTP Data Channels?', printVal(DetectRTC.isSctpDataChannelsSupported));
                    appendTR('Browser Supports RTP Data Channels?', printVal(DetectRTC.isRtpDataChannelsSupported));
                    appendTR('This page Supports Screen Capturing API?', printVal(DetectRTC.isScreenCapturingSupported));

                    appendTR('Is this browser supports multi-monitor selection & capturing screen of any monitor?', printVal(DetectRTC.isMultiMonitorScreenCapturingSupported));


                    appendTR('Is it a mobile device?', printVal(DetectRTC.isMobileDevice));
                    appendTR('Browser Supports WebSockets?', printVal(DetectRTC.isWebSocketsSupported));
                    
                    appendTR('Is Browser Supports Stream Capturing from Canvas?', printVal(DetectRTC.isVideoSupportsStreamCapturing));
                    appendTR('Is Browser Supports Stream Capturing from Video?', printVal(DetectRTC.isVideoSupportsStreamCapturing));

                    appendTR('Is Browser Supports Promises?', printVal(DetectRTC.isPromisesSupported));

                    appendTR('<strong>MediaStream.prototype:</strong><br>' + DetectRTC.MediaStream.toString().split(',').join(', '), DetectRTC.MediaStream === false ? 'Nope' : 'Yep');
                    appendTR('<strong>MediaStreamTrack.prototype:</strong><br>' + DetectRTC.MediaStreamTrack.toString().split(',').join(', '), DetectRTC.MediaStreamTrack === false ? 'Nope' : 'Yep');
                    appendTR('<strong>RTCPeerConnection.prototype:</strong><br>' + DetectRTC.RTCPeerConnection.toString().split(',').join(', '), DetectRTC.RTCPeerConnection === false ? 'Nope' : 'Yep');

                    /*
                    DetectRTC.DetectLocalIPAddress(function(ipAddress) {
                        if (ipAddress.indexOf('Local') !== -1) {
                            appendTR('Your <strong>Local</strong> IP Address', ipAddress);
                        } else {
                            appendTR('Your <strong>Public</strong> IP Address', ipAddress);
                        }
                    });
                    */

                    document.getElementById('infoIcon-set-sink-id').onclick = function() {
                        var pre = this.parentNode.querySelector('pre');
                        if(pre) {
                            pre.parentNode.removeChild(pre);
                            return;
                        }

                        pre = document.createElement('pre');
                        pre.className = 'sh_javascript inline-pre';
                        pre.innerHTML += '\
var secondarySpeakers = DetectRTC.audioOutputDevices[1];<br>\
if(secondarySpeakers) {<br>\
   videoElement.setSinkId(secondarySpeakers.deviceId);<br>\
}';
                        this.parentNode.appendChild(pre);
                        pre.focus();
                        sh_highlightDocument();
                    };

                    document.getElementById('infoIcon-apply-constraints').onclick = function() {
                        var pre = this.parentNode.querySelector('pre');
                        if(pre) {
                            pre.parentNode.removeChild(pre);
                            return;
                        }

                        pre = document.createElement('pre');
                        pre.className = 'sh_javascript inline-pre';
                        pre.innerHTML += '\
var secondaryCamera = DetectRTC.videoInputDevices[1];<br>\
if(secondaryCamera) {<br>\
   var videoConstraints = {<br>\
       deviceId: secondaryCamera.deviceId<br>\
   };<br>\
<br>\
   oldMediaStream.getVideoTracks().forEach(function(track) {<br>\
       track.applyConstraints(videoConstraints); <br>\
   });<br>\
}';
                        this.parentNode.appendChild(pre);
                        pre.focus();
                        sh_highlightDocument();
                    };

                    document.getElementById('infoIcon-replace-tracks').onclick = function() {
                        var pre = this.parentNode.querySelector('pre');
                        if(pre) {
                            pre.parentNode.removeChild(pre);
                            return;
                        }

                        pre = document.createElement('pre');
                        pre.className = 'sh_javascript inline-pre';
                        pre.innerHTML += '\
var newTrack = newMediaStream.getVideoTracks()[0];<br>\
var rtpSenders = rtcPeerConnection.getSenders()[0];<br>\
rtpSenders.replaceTrack(newTrack);';
                        this.parentNode.appendChild(pre);
                        pre.focus();
                        sh_highlightDocument();
                    };
                }

                function reloadDetectRTC() {
                    DetectRTC.load(onDetectRTCLoaded);
                }

                DetectRTC.load(function() {
                    reloadDetectRTC();

                    if(DetectRTC.MediaDevices[0] && DetectRTC.MediaDevices[0].label === 'Please invoke getUserMedia once.') {
                        navigator.mediaDevices.getUserMedia({audio: true, video: true}).then(reloadDetectRTC).catch(reloadDetectRTC);
                        return;
                    }

                    onDetectRTCLoaded();
                });

                document.getElementById('save').onclick = function() {
                    this.parentNode.removeChild(this);

                    html2canvas(browserFeaturesTable.parentNode, {
                        grabMouse: false,
                        onrendered: function(canvas) {
                            var image = canvas.toDataURL('image/jpeg');
                            // document.write('<img src="' + image +'">');
                            SaveToDisk(image);
                        }
                    });
                };

                function SaveToDisk(dataURL) {
                    if (DetectRTC.browser.isEdge) {
                        var blob = dataURLToBlob(dataURL);
                        if (typeof navigator.msSaveOrOpenBlob !== 'undefined') {
                            return navigator.msSaveOrOpenBlob(blob, 'DetectRTC.jpg');
                        } else if (typeof navigator.msSaveBlob !== 'undefined') {
                            return navigator.msSaveBlob(blob, 'DetectRTC.jpg');
                        }
                    }

                    var hyperlink = document.createElement('a');
                    hyperlink.href = dataURL;
                    hyperlink.target = '_blank';
                    hyperlink.download = 'DetectRTC.jpg';

                    if (DetectRTC.browser.name === 'Firefox') {
                        hyperlink.onclick = function() {
                            (document.body || document.documentElement).removeChild(hyperlink);
                        };
                        (document.body || document.documentElement).appendChild(hyperlink);
                    }

                    var evt = new MouseEvent('click', {
                        view: window,
                        bubbles: true,
                        cancelable: true
                    });

                    hyperlink.dispatchEvent(evt);

                    if (DetectRTC.browser.name === 'Firefox') {
                        URL.revokeObjectURL(hyperlink.href);
                    }
                }

                function dataURLToBlob(dataURL) {
                    var BASE64_MARKER = ';base64,';
                    if (dataURL.indexOf(BASE64_MARKER) == -1) {
                        var parts = dataURL.split(',');
                        var contentType = parts[0].split(':')[1];
                        var raw = decodeURIComponent(parts[1]);

                        return new Blob([raw], {
                            type: contentType
                        });
                    }

                    var parts = dataURL.split(BASE64_MARKER);
                    var contentType = parts[0].split(':')[1];
                    var raw = window.atob(parts[1]);
                    var rawLength = raw.length;

                    var uInt8Array = new Uint8Array(rawLength);

                    for (var i = 0; i < rawLength; ++i) {
                        uInt8Array[i] = raw.charCodeAt(i);
                    }

                    return new Blob([uInt8Array], {
                        type: contentType
                    });
                }
            </script>

            <section class="experiment own-widgets">
                <h2 class="header" id="updates" style="color: red; padding-bottom: .1em;"><a href="https://github.com/muaz-khan/DetectRTC/issues" target="_blank">DetectRTC Issues</a>
                </h2>
                <div id="github-issues"></div>
            </section>
        
            <section class="experiment">
                <h2 class="header" id="feedback">Feedback</h2>
                <div>
                    <textarea id="message" style="border: 1px solid rgb(189, 189, 189); height: 8em; margin: .2em; outline: none; resize: vertical; width: 98%;" placeholder="Have any message? Suggestions or something went wrong?"></textarea>
                </div>
                <button id="send-message" style="font-size: 1em;">Send Message</button><small style="margin-left: 1em;">Enter your email too; if you want "direct" reply!</small>
            </section>
            
            <section class="experiment">
                <h2 class="header">
                    How to use <a href="https://github.com/muaz-khan/DetectRTC" target="_blank">DetectRTC</a>?</h2>
                <pre class="sh_html">
&lt;script src="https://cdn.WebRTC-Experiment.com/DetectRTC.js"&gt;&lt;/script&gt;
</pre>
            </section>
            <section class="experiment">
                <pre class="sh_javascript">
DetectRTC.load(function() {
    DetectRTC.hasWebcam (has webcam device!)
    DetectRTC.hasMicrophone (has microphone device!)
    DetectRTC.hasSpeakers (has speakers!)
    DetectRTC.isScreenCapturingSupported
    DetectRTC.isSctpDataChannelsSupported
    DetectRTC.isRtpDataChannelsSupported
    DetectRTC.isAudioContextSupported
    DetectRTC.isWebRTCSupported
    DetectRTC.isDesktopCapturingSupported
    DetectRTC.isMobileDevice
    
    DetectRTC.isWebSocketsSupported
    DetectRTC.isWebSocketsBlocked
    DetectRTC.checkWebSocketsSupport(callback)

    DetectRTC.isWebsiteHasWebcamPermissions        // getUserMedia allowed for HTTPs domain in Chrome?
    DetectRTC.isWebsiteHasMicrophonePermissions    // getUserMedia allowed for HTTPs domain in Chrome?

    DetectRTC.audioInputDevices    // microphones
    DetectRTC.audioOutputDevices   // speakers
    DetectRTC.videoInputDevices    // cameras
    
    DetectRTC.osName
    DetectRTC.osVersion
    
    DetectRTC.browser.name === 'Edge' || 'Chrome' || 'Firefox'
    DetectRTC.browser.version
    DetectRTC.browser.isChrome
    DetectRTC.browser.isFirefox
    DetectRTC.browser.isOpera
    DetectRTC.browser.isIE
    DetectRTC.browser.isSafari
    DetectRTC.browser.isEdge

    DetectRTC.browser.isPrivateBrowsing // incognito or private modes

    DetectRTC.isCanvasSupportsStreamCapturing
    DetectRTC.isVideoSupportsStreamCapturing

    DetectRTC.DetectLocalIPAddress(callback)
});
</pre>

            </section>
            
            <section class="experiment own-widgets latest-commits">
                <h2 class="header" id="updates" style="color: red; padding-bottom: .1em;"><a href="https://github.com/muaz-khan/DetectRTC/commits/master" target="_blank">Latest Updates</a></h2>
                <div id="github-commits"></div>
            </section>  
        </article>
        
        <a href="https://github.com/muaz-khan/DetectRTC" class="fork-left"></a>
        
        <footer>
            <p>
                <a href="https://www.webrtc-experiment.com/">WebRTC Experiments</a>
                © <a href="https://plus.google.com/+MuazKhan" rel="author" target="_blank">Muaz Khan</a>
                <a href="mailto:muazkh@gmail.com" target="_blank">muazkh@gmail.com</a>
            </p>
        </footer>
    
        <!-- commits.js is useless for you!  -->
        <script>
            window.useThisGithubPath = 'muaz-khan/DetectRTC';
        </script>
        <script src="https://cdn.webrtc-experiment.com/commits.js" async> </script>
    </body>
</html>
